#include "server.h"
//信号关闭进程
int exitPipe[2];
void handler(int signum){
    printf("signum = %d\n", signum);
    write(exitPipe[1], "1", 1);
}

int main(int argc, char* argv[])
{
    ARGS_CHECK(argc,4);

    pipe(exitPipe);
    if(fork()){
        //父进程
        close(exitPipe[0]);
        signal(SIGUSR1,handler);
        wait(NULL);
        printf("Parent is going to exit.\n");
        exit(0);
    }
    close(exitPipe[1]);

    //TCP准备工作,建立线程池
    int sockfd = initTCP(argv[1],argv[2]);
    int epfd = epoll_create(1);
    epollAdd(epfd,sockfd);
    epollAdd(epfd,exitPipe[0]);
    threadPool_t threadPool;
    int workerNum = atoi(argv[3]);
    threadPoolInit(&threadPool,workerNum);
    makeWorker(&threadPool);

    //连接数据库
    MYSQL* mysql = mysql_init(NULL);
    MYSQL* ret = mysql_real_connect(mysql,
                                    "localhost",
                                    "root",
                                    "3692580147",
                                    "diskbyself",
                                    0,NULL,0);
    if(ret == NULL){
        fprintf(stderr,"%s:%s\n","mysql_real_connect",mysql_error(mysql));
    }
    LOG_ERROR_CHECK(ret,NULL,"mysql_real_connect");
    printf("主线程数据库连接成功。\n");

    //建立时间轮
    timing_wheel_t timing_wheel;
    bzero(&timing_wheel,sizeof(timing_wheel));

    //epoll
    struct epoll_event readySet[1024];
    train_t train;
    time_t cur_time = time(NULL);
    time_t last_time = cur_time;
    while(1){
        int readyNum = epoll_wait(epfd, readySet, 1024, 1000);
        cur_time = time(NULL);
        if(cur_time - last_time > 1){
            timing_wheel.cur = (timing_wheel.cur + 1) % TIMEOUT;
            find_set_bits_fast(timing_wheel.bitmap,epfd,timing_wheel.cur);
            last_time = cur_time;
        }
        for (int i = 0; i < readyNum; ++i){
            if(readySet[i].data.fd == sockfd){
                int netfd = accept(sockfd, NULL, NULL);
                //检查是客户端还是长命令线程
                recvn(netfd,&train,sizeof(train));
                if(train.tag == LOGIN){
                    epollAdd(epfd,netfd);
                    set_bit(timing_wheel.bitmap[timing_wheel.cur],netfd);
                    printf("A client connect, netfd = %d.\n", netfd);
                }else if(train.tag == TOKEN){
                    //唤醒子线程
                    pthread_mutex_lock(&threadPool.mutex);
                    enQueue(&threadPool.taskQueue, netfd);
                    pthread_cond_signal(&threadPool.cond);
                    pthread_mutex_unlock(&threadPool.mutex);
                }else{
                    close(netfd);
                }
            }
            else if(readySet[i].data.fd == exitPipe[0]){//有序退出
                pthread_mutex_lock(&threadPool.mutex);
                threadPool.exitFlag = 1;
                pthread_cond_broadcast(&threadPool.cond);
                pthread_mutex_unlock(&threadPool.mutex);

                for (int j =0; j < workerNum; ++j){
                    pthread_join(threadPool.tidArr.arr[j],NULL);
                }
                printf("main is going to exit.\n");
                exit(0);
            }else{//业务
                bzero(&train,sizeof(train));
                int ret = recvn(readySet[i].data.fd,&train,sizeof(train));
                if(ret == -1){
                    printf("A client disconnected.\n");
                    syslog(LOG_INFO,"netfd %d is disconnected",readySet[i].data.fd);
                    for(int j = 0; j < TIMEOUT; ++j){
                        clear_bit(timing_wheel.bitmap[j],readySet[i].data.fd);
                    }
                    epollDel(epfd,readySet[i].data.fd);
                    continue;
                }
                if(train.tag == USRNAME){
                    char usrname[128] = {0};
                    memcpy(usrname,train.data,train.length);
                    printf("usrname = %s.\n",usrname);
                    login_send_salt(readySet[i].data.fd,usrname,mysql);
                    usrinfoInsert(&threadPool.usrinfo,usrname,readySet[i].data.fd);
                }
                else if(train.tag == ENCRYPTED){
                    char encrypted_passwordusr[100];
                    memcpy(encrypted_passwordusr,train.data,train.length);
                    char usrname[128];
                    netfd_to_usrname(&threadPool.usrinfo,readySet[i].data.fd,usrname);
                    printf("usrname = %s.\n",usrname);
                    int ret = login_check(readySet[i].data.fd,usrname,encrypted_passwordusr,mysql);
                    if(ret == 0){
                        //计算token并发给客户端
                        char token[200] = {0};
                        gettoken(usrname,token);
                        train.tag = TOKEN;
                        train.length = strlen(token) + 1;
                        memcpy(train.data,token,train.length);
                        send(readySet[i].data.fd,&train,sizeof(train),MSG_NOSIGNAL);
                        inserttoken(&threadPool.usrinfo,readySet[i].data.fd,token);

                        stackDir_t* dirStack = netfd_to_dirStack(&threadPool.usrinfo,readySet[i].data.fd);
                        printf("usrname = %s\n",usrname);
                        char sql[1024] = {0};
                        sprintf(sql,"select id from file where path = '/' AND user_name = '%s';",usrname);
                        printf("%s\n",sql);
                        int qret = mysql_query(mysql,sql);
                        if(qret != 0){
                            fprintf(stderr,"%s:%s\n","mysql_query",mysql_error(mysql));
                        }
                        MYSQL_RES* result = mysql_store_result(mysql);
                        MYSQL_ROW row = mysql_fetch_row(result);
                        int cur = atoi(row[0]);
                        push(dirStack,cur);
                    }
                }else{//其他业务
                    for(int j = 0; j < TIMEOUT; ++j){
                        clear_bit(timing_wheel.bitmap[j],readySet[i].data.fd);
                    }
                    set_bit(timing_wheel.bitmap[timing_wheel.cur],readySet[i].data.fd);

                    char pathName[512] = {0};
                    memcpy(pathName,train.data,train.length);
                    char usrname[128];
                    netfd_to_usrname(&threadPool.usrinfo,readySet[i].data.fd,usrname);
                    stackDir_t* dirStack = netfd_to_dirStack(&threadPool.usrinfo,readySet[i].data.fd); 
                    switch(train.tag){
                    case CD:
                            cd(dirStack,pathName,usrname,mysql);
                            syslog(LOG_INFO,"%s cd %s.",usrname,pathName);
                            break;
                    case LS:{
                                char** result;
                                result = ls(dirStack,mysql);
                                sendLS(readySet[i].data.fd,result);
                                freeLS(result);
                                syslog(LOG_INFO,"%s ls.",usrname);
                                break;
                            }
                    case PWD:{
                                 char pwds[512] = {0};
                                 pwd(dirStack,mysql,pwds);
                                 sendPWD(readySet[i].data.fd,pwds);
                                 syslog(LOG_INFO,"%s pwd.",usrname);
                                 break;
                             }
                    case MKDIR:
                             my_mkdir(dirStack,mysql,pathName,usrname);
                             syslog(LOG_INFO,"%s mkdir %s.",usrname,pathName);
                             break;
                    case RMDIR:
                             my_rmdir(dirStack,mysql,pathName,usrname);
                             syslog(LOG_INFO,"%s rmdir %s.",usrname,pathName);
                             break;
                    case REMOVE:
                             removefile(pathName,dirStack,usrname,mysql); 
                             syslog(LOG_INFO,"%s remove %s.",usrname,pathName);
                             break;
                    case QUIT:
                             break;
                    default:
                             printf("ERROR: try again!\n");
                    }
                }
            }
        }
    }
    return 0;
}
